將昨天產生產生的訊息文本,傳送至測試伺服器https://apisbx.sinopac.com/funBIZ/QPay.WebAPI/api/Order,如果沒有問題的話會收到如下訊息:
{
"Version": "1.0.0",
"ShopNo": "NA0249_001",
"APIService": "OrderCreate",
"Sign": "FBF41F9BFA5607F5141D508DA6B914DCAB97CE7CDE22EE636C9FDD81A9AC3277",
"Nonce": "NjM3Njc0NzY1MDExMjc6MjVmMDE2NDUyYThjNDE4ODY1MmI4Mzk0OGM3YWY1Mjg0M2Y4NDdlMjAyMTk0YWM3MGFhZGZmNDcyMGQ5ZjhjNw==",
"Message": "FAE9E297DB4D0B16D3E5A56561B28DDC41CD135B23D5309F971091033425405BA96669FE5A27B1D42DC4EB9636EBB0D9D9D618BC2B3969124A4F73CBB760CB83084623E1C2DED846BB46525E1B74F187EEF42A0F483AC49B0A12268D28452F44D268D38BDB91C464B74B1BB80D6DFC372622D8006005B0ABF5637287CB587FCE6ABB9D2BA377A29EC2E7E696CFDE2E305739CF2E6CBC1F2B71741064CA21CE3A6C6BFBAD663140A4CCC5AB24BE77569A26E1EA3A71EC2BC7AFC6E0F43ED537E42CDF535E910E25413BF4BC649D800F592FEA277BA18BF312EDD9A062D7F24A6405AC01EEF3F7F55EBC5978EEFC7AB097A802A1D05B675CC08E5ABD3FD9106EE0C624839EB0451EBE0F10E85C6DFCE4C9E0D29B3E633928F1A73102C04FA9DB91D7391D8917DC263437DBC50A7ACAF2CA06F8114669F783EF5189925B61EC9D7ECC3C504D09996665BC7CD3C3725F5D778F1D843FC42183153E565BF06307405F30401BA7E83EFAC91B54612D92E284F3BCFE324E26F8E7BFB1AE6326D96E2513D53A4D25DC1C1C24437A403A5BF281DA95A4EE018D6224F18128EB5FBFF3EF73EEE19E0EFD9429AB2976AB70C8DB050EFD81DB591831FD820157ACC4B60F101ABEF75AAA420EFF4C6FD7495226872B410CB87E958E7C92BFF3E3D35B4367B927F167A30F495876E82428ACC0D51BD61C7D30DB0A5FAEA26CDA24F79EA132000FFCF9C2E6EBCA124C6D8F546DF954C333"
}
通過先前產生訊息文本的相同方式進行解密(但本回Nonce由回應中的欄位取得),分別得出
將Message欄位取出,以Hex方式讀取,同樣用AES-CBC(256bits)方式進行解密,會得出
{"OrderNo":"2021091500002","ShopNo":"NA0249_001","TSNo":"NA024900000173","Amount":40400,"Status":"S","Description":"S0000 – 處理成功","Param1":"","Param2":"","Param3":"","PayType":"C","CardParam":{"CardPayURL":"https://sandbox.sinopac.com/QPay.WebPaySite/Bridge/PayCard?TD=NA024900000173&TK=a135ccb2-2e4c-4af4-b4ef-dfece6367731"}}
def AES_CBC_Decrypt(HashID, iv, data):
try:
key = str.encode(HashID)
iv = str.encode(iv)
data = bytes.fromhex(data)
cipher = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
pt = unpad(cipher.decrypt(data), AES.block_size)
return pt.decode("utf-8")
except (ValueError, KeyError):
print("Incorrect decryption")
undecrypt_msg = js_resp['Message']
msg = AES_CBC_Decrypt(HashID, iv=iv, data=undecrypt_msg)
看到這邊,你一定想哇成功了對不對,其實還沒有,還少了最後一步,簽名驗證(Sign),收發的資訊傳輸過程都需要驗證訊息文本與金鑰的配對,以確認資料的防竄改,關於如何產生Sign,可以參照Day4,在這邊我們要取訊息文本中的Sign,與解密後Message中的參數進行比對是否相同
def GetRespSign(msg:str, nonce:str, HashID:str):
msg = json.loads(msg)
SignStr = ""
for parm in sorted(msg, key=lambda v: v.upper()):
val = msg[parm]
if(type(val) == dict or not val):continue
SignStr = SignStr + f"{parm}={msg[parm]}&"
SignStr = SignStr.removesuffix('&') + nonce + HashID
sign = hashlib.sha256(SignStr.encode('utf-8')).hexdigest().upper()
return sign
resp_vsign = js_resp['Sign']
resp_csign = GetRespSign(msg=msg, nonce=nonce, HashID=HashID)
if(resp_vsign == resp_csign):
print("簽章檢驗成功")
return true
else:
print("簽章驗證失敗")
return false
呼,到現在已經將大致上的API如何使用完成時做了,接下來準備小跑進入實作環節,搭建一個儲值卡系統希望有時間做得完